
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Boolean.h"
#include "Number.h"
#include "Object.h"
#include "String.h"

Object* Number::prototype = new Object(new Number(0), Object::prototype,
        new String("Number"));

double sign(double val)
{
    return val > 0 ? 1 : -1;
}

Number::Number(const char* cstr, int length)
{
    m_value = atof(cstr);
}

Number::Number(double value)
    : m_value(value)
{}

ValueType Number::getType() const
{
    return VT_NUMBER;
}

Boolean* Number::toBoolean()
{
    // The result is false if the argument is +0, -0, or NaN
    // otherwise the result is true
    return m_value == 0 || isnan(m_value) ? Boolean::False : Boolean::True;
}

Number* Number::toNumber()
{
    return this;
}

Object* Number::toObject()
{
    return new Object(this, Number::prototype, new String("Number"));
}

Number* Number::toInteger()
{
    // Ecma 9.5
    // 2. If we are NaN, +0, -0, +Inf, -Inf, return +0
    if (isnan(m_value) || isinf(m_value) || m_value == 0)
    {
        return new Number(0);
    }

    // 3. Compute sign(result(1)) * floor(abs(result(1)))
    double dres = sign(m_value) * floor(fabs(m_value));

    // 4. Return dres
    return new Number(dres);
}

Number* Number::toInt32()
{
    // Ecma 9.5
    // 2. If we are NaN, +0, -0, +Inf, -Inf, return +0
    if (isnan(m_value) || isinf(m_value) || m_value == 0)
    {
        return new Number(0);
    }

    // 3. Compute sign(Result(1) * floor(abs(result(1)))
    double dres = sign(m_value) * floor(fabs(m_value));
    
    // 4. Compute... blah blah
    // TODO: is this right?
    int ires = (int)dres;

    // 5. Return the result
    return new Number((double)ires);
}

Number* Number::toUint32()
{
    // Ecma 9.6
    // 2. If we are NaN, +0, -0, +Inf, -Inf, return +0
    if (isnan(m_value) || isinf(m_value) || m_value == 0)
    {
        return new Number(0);
    }

    // 3. Compute sign(val) * floor(abs(val))
    double dres = sign(m_value) * floor(fabs(m_value));

    // 4. Compute res % 2^32. Err.. convert to an unsigned integer?
    // TODO: is this right?
    unsigned int ires = (unsigned int)dres;

    // 5. Return the result
    return new Number((double)ires);
}

Number* Number::toUint16()
{
    // Ecma 9.7 
    // 2. If we are NaN, +0, -0, +Infinity, -Infinity, return 0
    if (isnan(m_value) || isinf(m_value) || m_value == 0)
    {
        return new Number(0);
    }

    // 3. Compute sign(val) * floor(fabs(m_value));
    double dres = sign(m_value) * floor(fabs(m_value));

    // 4. blah blah
    // TODO: is this right?
    unsigned short ires = (unsigned short)dres;

    // 5. Return the result
    return new Number((double)ires);
}

String* Number::toString()
{
    // Ecma 9.8.1
    // 1. If m is NaN, return "NaN"
    if (isnan(m_value))
    {
        return new String("NaN");
    }

    // 2. If m is +0 or -0, return the string "0";
    if (m_value == 0)
    {
        return new String("0");
    }

    // 3. If m is less than zero, return the string concatenation of the
    // string "-" and ToString(-m); 
    if (m_value < 0)
    {
        String* mnum = (new Number(-m_value))->toString();
        String* str = new String("-");
        return String::add(str, mnum);
    }

    // 4. If m is infinity, return "Infinity"
    if (isinf(m_value))
    {
        return new String("Infinity");
    }

    // 5. Otherwise, let n, k, s be integers such that k >= 1, 10^(k-1)
    // and it goes on, not sure what it says... TODO: figure that out.
    char buf[1024];
    if (floor(m_value) == m_value)
    {
        // This is an integer
        snprintf(buf, 1024, "%llu", (unsigned long long)m_value);
    }
    else
    {
        // A double.
        snprintf(buf, 1024, "%f", m_value);

        // Chop off any traling zeros.
        int len = strlen(buf);
        for (int i = len-1; i >= 0 && buf[i] == '0'; i--)
        {
            buf[i] = '\0';
        }

    }
    return new String(buf);

}

Number* Number::multiply(Number* lhs, Number* rhs)
{
    return new Number(lhs->m_value * rhs->m_value);
}

Number* Number::divide(Number* lhs, Number* rhs)
{
    return new Number(lhs->m_value / rhs->m_value);
}

Number* Number::modulo(Number* lhs, Number* rhs)
{
    return new Number(fmod(lhs->m_value, rhs->m_value));
}

Number* Number::left_shift(Number* lhs, Number* rhs)
{
    // Ecma 11.7.1
    // 5. Call toInt32 on lhs
    // 6. Call toUint32 on rhs
    int ilhs = (int)lhs->toInt32()->m_value;
    unsigned int urhs = (unsigned int)rhs->toUint32()->m_value;

    // 7. Mask out all but the least significant 5 bits of rhs
    urhs = urhs & 0x1F;

    return new Number((double)(int)(ilhs << urhs));
}

Number* Number::right_shift(Number* lhs, Number* rhs)
{
    // Ecma 11.7.2
    // 5. Call toInt32 on lhs
    // 6. Call toUint32 on rhs
    int ilhs = (int)lhs->toInt32()->m_value;
    unsigned int urhs = (unsigned int)rhs->toUint32()->m_value;

    // 7. Mask out all but the least significant 5 bits of rhs
    urhs = urhs & 0x1F;

    return new Number((double)(int)(ilhs >> urhs));
}

Number* Number::unsigned_right_shift(Number* lhs, Number* rhs)
{
    // Ecma 11.7.3
    // 5. Call toUint32 on lhs
    // 6. Call toUint32 on rhs
    //
    unsigned int ulhs = (unsigned int)lhs->toUint32()->m_value;
    unsigned int urhs = (unsigned int)rhs->toUint32()->m_value;

    urhs = urhs & 0x1F;
    return new Number((double)(unsigned int)(ulhs >> urhs));
}

Number* Number::add(Number* lhs, Number* rhs)
{
    return new Number(lhs->m_value + rhs->m_value);
}

Number* Number::subtract(Number* lhs, Number* rhs)
{
    return new Number(lhs->m_value - rhs->m_value);
}

Number* Number::bitwise_and(Number* lhs, Number* rhs)
{
    int ilhs = (int)lhs->toInt32()->m_value;
    int irhs = (int)rhs->toInt32()->m_value;
    return new Number((double)(ilhs & irhs));
}

Number* Number::bitwise_or(Number* lhs, Number* rhs)
{
    int ilhs = (int)lhs->toInt32()->m_value;
    int irhs = (int)rhs->toInt32()->m_value;
    return new Number((double)(ilhs | irhs));
}

Number* Number::bitwise_xor(Number* lhs, Number* rhs)
{
    int ilhs = (int)lhs->toInt32()->m_value;
    int irhs = (int)rhs->toInt32()->m_value;
    return new Number((double)(ilhs ^ irhs));
}

Number* Number::bitwise_not(Number* rhs)
{
    return new Number((double)(~(int)(rhs->m_value)));
}

Number* Number::minus(Number* rhs)
{
    return new Number(-rhs->m_value);
}

Boolean* Number::less_than(Number* lhs, Number* rhs)
{
    return (lhs->m_value < rhs->m_value) ? Boolean::True : Boolean::False;
}

Boolean* Number::equals(Number* lhs, Number* rhs)
{
    return lhs->m_value == rhs->m_value ? Boolean::True : Boolean::False;
}

double Number::get_value() const
{
    return m_value;
}

